#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <mysql/mysql.h>
#include <errno.h>

#include "../inc/cJSON.h"
#include "../inc/tcp.h"
#include "../inc/agreement.h"
#include "../inc/login.h"

static int is_valid_fd(int fd);

// 处理群聊命令：向所有在线用户转发消息（排除发送者）
void handle_chat_room_cmd(client_info_p client, cJSON *root, cJSON *response)
{
    int fd_count = 0;
    int *online_fds = get_all_online_fds(client, &fd_count);

    // 提取原有根对象中的关键字段
    cJSON *type_item = cJSON_GetObjectItem(root, "type");
    const char *type_value = NULL;
    if (type_item && type_item->type == cJSON_String)
    {
        type_value = type_item->valuestring;
    }

    cJSON *payload_item = cJSON_GetObjectItem(root, "payload");
    cJSON *new_root = cJSON_CreateObject();

    // 按顺序添加字段
    if (type_value)
    {
        cJSON_AddStringToObject(new_root, "type", type_value);
    }
    cJSON_AddNumberToObject(new_root, "code", 1);
    cJSON_AddStringToObject(new_root, "message", "收到聊天室消息");

    if (payload_item)
    {
        cJSON_AddItemToObject(new_root, "payload", cJSON_Duplicate(payload_item, 1));
    }

    char resp_buf[1024] = {0};
    generate_response_string(new_root, resp_buf);
    cJSON_Delete(new_root); // 释放新创建的JSON对象，避免内存泄漏

    // 遍历所有有效 fd，向除发送者外的用户转发消息
    int send_success = 0;
    for (int i = 0; i < fd_count; i++)
    {
        int target_fd = online_fds[i];

        if (target_fd == client->connfd) // 排除发送者自身的 fd
        {
            continue;
        }
        send_message_to_client(target_fd, resp_buf);
        send_success++;
    }

    // 构建成功响应
    cJSON_AddNumberToObject(response, "code", CHATROOM_CHAT_SUCCESS_RECV_FROM_CLIENT);
    cJSON_AddStringToObject(response, "message", "消息发送成功");

    free(online_fds);
}

// 获取所有在线用户的文件描述符
int *get_all_online_fds(client_info_p client, int *fd_count)
{

    char sql[512] = {0};
    MYSQL_RES *result = NULL;
    MYSQL_ROW row;
    int *online_fds = NULL;
    int count = 0;    // 有效 fd 计数
    int capacity = 4; // 动态数组初始容量

    // 检查并修复数据库连接（避免连接失效导致查询失败）
    if (mysql_check_and_reconnect(&client->db_conn) != 0)
    {
        *fd_count = -1;
        printf("[FD] 数据库连接失效，重连失败\n");
        return NULL;
    }

    // 初始化动态数组（存储 fd）
    online_fds = (int *)malloc(sizeof(int) * capacity);
    if (!online_fds)
    {
        *fd_count = -1;
        printf("[FD] 动态数组 malloc 失败（内存不足）\n");
        return NULL;
    }

    //  构造 SQL：查询所有非空的 connfd
    snprintf(sql, sizeof(sql), "SELECT connfd FROM online_user WHERE connfd IS NOT NULL");

    // 执行 SQL 查询
    if (mysql_query(client->db_conn, sql) != 0)
    {
        printf("[FD] 查询在线 fd 失败：%s(SQL:%s)\n", mysql_error(client->db_conn), sql);
        goto cleanup; // 查询失败，跳转到资源清理
    }

    // 获取结果集
    result = mysql_store_result(client->db_conn);
    if (!result)
    {
        printf("[FD] 获取结果集失败：%s\n", mysql_error(client->db_conn));
        goto cleanup;
    }

    // 遍历结果集，提取并校验有效 fd
    while ((row = mysql_fetch_row(result)) != NULL)
    {
        // 校验 row[0] 非空（避免 atoi 转换空指针）
        if (!row[0])
            continue;

        // 转换 connfd 为整数（若 connfd 是字符串类型，需确保格式正确）
        int fd = atoi(row[0]);

        // 校验 fd 有效性（调用 is_valid_fd，排除关闭/无效的 fd）
        if (is_valid_fd(fd))
        {
            // 动态数组扩容：当计数达到容量时，翻倍扩容（避免频繁 realloc）
            if (count >= capacity)
            {
                int new_capacity = capacity * 2;
                int *new_fds = (int *)realloc(online_fds, sizeof(int) * new_capacity);
                if (!new_fds)
                {
                    printf("[FD] 动态数组 realloc 失败（内存不足）\n");
                    goto cleanup;
                }
                online_fds = new_fds;
                capacity = new_capacity;
            }
            online_fds[count++] = fd; // 存入有效 fd，计数+1
        }
        else
        {
            // 无效 fd：可选项——从数据库删除该记录（避免下次重复查询）
            printf("[FD] 清理无效 fd: %d(已关闭或不存在)\n", fd);
            char del_sql[256] = {0};
            snprintf(del_sql, sizeof(del_sql), "DELETE FROM online_user WHERE connfd = %d", fd);
            mysql_query(client->db_conn, del_sql); // 非关键操作，无需处理失败
        }
    }

    // 优化数组大小：释放多余内存（将数组缩为实际有效 fd 数量）
    int *final_fds = (int *)realloc(online_fds, sizeof(int) * count);
    if (final_fds)
    {
        online_fds = final_fds; // 扩容成功，使用新地址（失败则保留原地址，不影响使用）
    }

    // 处理成功：设置输出参数并返回数组
    *fd_count = count;
    printf("[FD] 成功获取 %d 个有效在线 fd\n", count);
    return online_fds;

// 资源清理：统一释放动态数组和结果集
cleanup:
    if (result)
        mysql_free_result(result); // 释放数据库结果集
    if (online_fds)
        free(online_fds); // 释放动态数组
    *fd_count = -1;       // 标记失败状态
    return NULL;
}

// 校验文件描述符（fd）是否有效（已连接且未关闭）
static int is_valid_fd(int fd)
{
    if (fd <= 2)
    {
        return 0;
    }

    int error = 0;
    socklen_t len = sizeof(error);
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0)
    {
        return 0;
    }

    //  检查 socket 是否可读且无数据（避免误判半关闭连接）
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(fd, &read_fds);

    struct timeval tv = {0, 0}; // 非阻塞检查，立即返回
    int ret = select(fd + 1, &read_fds, NULL, NULL, &tv);
    if (ret < 0)
    {
        return 0; // select 失败，判定为无效
    }
    else if (ret > 0)
    {
        // 有数据可读：尝试读取 1 字节，判断是否为关闭信号（recv 返回 0）
        char buf[1];
        ssize_t recv_len = recv(fd, buf, sizeof(buf), MSG_PEEK); // MSG_PEEK 不消费数据
        if (recv_len == 0)
        {
            return 0; // 对方关闭连接，判定为无效
        }
    }

    return 1; // 所有校验通过，fd 有效
}

void handle_join_chat_room_cmd(client_info_p client, cJSON *root, cJSON *response)
{

    int fd_count = 0;
    int *online_fds = get_all_online_fds(client, &fd_count);
    char resp_buf[1024] = {0};

    if (!client || !root || !response)
    {
        return;
    }

    // 解析 payload 子对象
    cJSON *payload = cJSON_GetObjectItem(root, "payload");
    if (!payload || !cJSON_IsObject(payload))
    {
        return;
    }

    cJSON *userid_json = cJSON_GetObjectItem(payload, "userid");
    if (!userid_json || !cJSON_IsString(userid_json) || strlen(userid_json->valuestring) == 0)
    {
        return;
    }
    char userid[32] = {0};
    strncpy(userid, userid_json->valuestring, sizeof(userid) - 1);

    //  解析 action
    cJSON *action_json = cJSON_GetObjectItem(payload, "action");
    if (!action_json || !cJSON_IsString(action_json))
    {
        return;
    }

    cJSON *new_root = cJSON_CreateObject();
    cJSON_AddStringToObject(new_root, "type", "join_chat_room");
    char message[128] = {0};
    if (strcmp(action_json->valuestring, "join") == 0)
    {
        sprintf(message, "用户[%s]加入聊天室", userid);
        cJSON_AddNumberToObject(new_root, "code", 1);
        cJSON_AddStringToObject(new_root, "message", message);
    }
    else if (strcmp(action_json->valuestring, "exit") == 0)
    {
        sprintf(message, "用户[%s]退出聊天室", userid);
        cJSON_AddNumberToObject(new_root, "code", 1);
        cJSON_AddStringToObject(new_root, "message", message);
        cJSON_AddStringToObject(new_root, "userid", userid);
    }

    generate_response_string(new_root, resp_buf);

    int send_success = 0;
    for (int i = 0; i < fd_count; i++)
    {
        int target_fd = online_fds[i];

        if (target_fd == client->connfd) // 排除发送者自身的 fd
        {
            continue;
        }
        send_message_to_client(target_fd, resp_buf);
        send_success++;
    }

    cJSON_AddNumberToObject(response, "code", CHATROOM_CHAT_SUCCESS_RECV_FROM_CLIENT);
    cJSON_AddStringToObject(response, "message", "服务器广播成功");

    free(online_fds);
}